const md5 = require('md5');
const fs = require('fs-extra');
// var slog = require('single-line-log').stdout;
const path = require('path');
const glob = require('glob');
const OssUtils = require('./ossUtils');
const async = require('async');
const parallelLimit = async.parallelLimit;
const cwd = require('process').cwd;
const uploadQueue = []; // async functions
const maxRetryCount = 6; // 重试次数
const config = loadConfig(__dirname, '../upload-oss.online.json'); // 默认线上配置项
const reqMultipart = '*';
// var reqMultipart = 'multipart/form-data;charset=UTF-8';

function UploadOss({
  uploadFrom,
  uploadTo,
  bucket,
  env,
  filename,
  limit,
  chunkSize,
  accessKeyId,
  accessKeySecret,
  success,
  fail
}) {
  const that = this;
  if (env === 'test') {
    config = loadConfig(__dirname, '../upload-oss.test.json'); // 默认线上配置项
  }
  that.ossUtils =new OssUtils({
    // yourregion填写Bucket所在地域。以华东1（杭州）为例，Region填写为oss-cn-hangzhou。
    region: config.region||'oss-cn-beijing',
    // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
    accessKeyId: accessKeyId || config.accessKeyId,
    accessKeySecret: accessKeySecret || config.accessKeySecret,
    // 填写Bucket名称。
    bucket: bucket || config.bucket
  });
  that.uploadFrom = uploadFrom; // 要上传的目标文件夹
  that.uploadTo = uploadTo.replace(/@/g, '%40'); // 带@符号的文件名会鉴权失败; // 要上传的目标文件夹
  that.bucket = bucket || config.bucket; // 要上传的目标文件夹
  that.filename = filename; // 要上传的目标文件夹
  that.chunkSize = (chunkSize || config.chunkSize || 5) * 1024 * 1024; // 分片上传时的大小限制
  that.fileIndex = 0; // 文件计数器
  that.successIndex = 0; // 上传成功文件计数器
  that.success = success; // 是否完成文件读取
  that.fail = fail; // 是否完成文件读取
  that.timer = setTimeout(() => { // 超时控制器
    if (that.successIndex < that.fileIndex) {
      console.error('文件上传超时; timeout: 5min;');
      that.fail && that.fail({
        type: 'timeout',
        msg: '文件上传超时; timeout: 5min;'
      });
    }
  }, 5 * 60 * 1000);
  that.retryList = {}; // 失败重试list   最多支持5次失败重传
  that.upload = function upload(dir = that.uploadFrom) { // 文件遍历器
    if (!this.uploadFrom || !this.uploadTo) { // 入参校验
      throw Error('【uploadFrom】【uploadTo】 为必传参数');
    }
    const pattern = path.join('**', '*.*');
    glob(pattern, {
      cwd: dir,
      dot: true
    }, function (er, files) {
      if (!files.length) {
        throw Error('【' + cwd() + path.sep + dir + '】中无可上传文件');
      } 
      that.fileIndex = files.length;
      files.forEach((item, index) => {
        const targetFile = path.join(dir, item);
        uploadQueue.push(function (callback) {
          // callback()
          that._uploadFile_(targetFile, item, (err, results) => {
            callback(err, results)
          })
        })
      })
      parallelLimit(uploadQueue, limit || config.limit, function (err, results) {
        if (err) {
          console.log(err);
        }
      })
    })
  }
  that.uploadFile = function upload(file) { // 单个文件
    if (!that.uploadTo) { // 入参校验
      throw Error('【uploadTo】 为必传参数');
    }
    that.fileIndex = 1;
    // var fileName = path.sep + path.basename(dir);
    const fileName = that.filename || path.basename(file);
    // console.log(fileName);
    const filePath = path.resolve(that.uploadFrom || process.cwd(), file);
    that._uploadFile_(filePath, fileName, (err, results) => { })
  }

  that.uploadFileData = function (...args) {
    that._uploadFile_(...args);
  }

  that._uploadFile_ = function _uploadFile_(targetFile, filename, callback) {

    filename = filename.replace(/@/g, '%40'); // 带@符号的文件名会鉴权失败
    filename = filename.replace(new RegExp('^/'), ''); // 排除首字符为/的路径
    filename = filename.replace(/[\u4e00-\u9fa5]+/g, str => encodeURIComponent(str));

    let uploadPath = `/${that.uploadTo}/${filename}`;
    // 支持上传文件到根路径
    if (that.uploadTo === '/') {
      uploadPath = `/${filename}`;
    }
    const status = fs.statSync(targetFile)    
    if (status.size  > that.chunkSize) {
      that.ossUtils.multipartUpload(uploadPath, {
        headers: {
          'Content-Type': '*' //可以设置image/jpeg，image/gif，*等
        },

        data: {
          url: targetFile, //上传的文件url
          bucket: that.bucket, //bucket name
          key: `${that.uploadTo}/${filename}`, //文件名称，可以携带具体文件夹，比如a/b/c.jpg 
        },
        success: function (res) { //成功回调
          that.countControl(filename, callback);
        },
        fail: function (err) { //失败回调
          console.error('大文件上传失败');
          console.log('错误 err', 'url=', uploadPath, err, '\n')
          if (that.retryList.hasOwnProperty(filename) && that.retryList[filename] > maxRetryCount) {
            console.error('文件上传重试失败');
            that.fail && that.fail({
              type: 'uploadFail',
              msg: '文件上传重试失败',
              err,
            });
            callback('file post failed');
          } else if (that.retryList.hasOwnProperty(filename)) {
            that.retryList[filename]++;
            _uploadFile_(targetFile, filename, callback);
          } else {
            that.retryList[filename] = 1;
            _uploadFile_(targetFile, filename, callback);
          }
          console.log(`第${that.retryList[filename]}次上传失败: `, filename);
        },
        onprogress: function () { } //进度回调
      });
    } else {
      // 直接传
      that.ossUtils.singleUpload(uploadPath, {
        data: {
          url: targetFile, //上传的文件url
        },

        success: res => {
          that.countControl(filename, callback);
        },
        fail: (err) => {
          console.log('错误 err', 'url=', uploadPath, err, '\n')
          if (that.retryList.hasOwnProperty(filename) && that.retryList[filename] > maxRetryCount) {
            console.error('文件上传重试失败');
            that.fail && that.fail({
              type: 'uploadFail',
              msg: '文件上传重试失败',
              err,
            });
            callback('file post failed');
          } else if (that.retryList.hasOwnProperty(filename)) {
            that.retryList[filename]++;
            _uploadFile_(targetFile, filename, callback);
          } else {
            that.retryList[filename] = 1;
            _uploadFile_(targetFile, filename, callback);
          }
          console.log(`第${that.retryList[filename]}次上传失败: `, filename);
        }
      })
    }


  }
  that.countControl = function (filename, callback) {
    that.successIndex++;
    if (that.successIndex === 1) {
      console.log('文件上传中')
    }
    console.log(`第${that.retryList[filename] && that.retryList[filename] + 1 || 1}次上传成功: `, filename)
    callback(null, {
      fileName: filename
    });
    if (that.successIndex === that.fileIndex) {
      try {
        that.success && that.success();
      } catch (e) {
        console.error('success callback error: ', e);
        process.reallyExit(1);
      }
      clearTimeout(that.timer);
    }
  }
}

module.exports = UploadOss;


function loadConfig(cwd, configPath) {
  let jsonFilePath = path.resolve(cwd, configPath);
  let jsonConfig = {};
  try {
    jsonConfig = fs.readJSONSync(jsonFilePath);
  } catch (error) {
    console.error(jsonFilePath, "not found");
    throw new Error(error);
  }
  return jsonConfig;
}